#include "Camera.h"
#pragma warning(disable : 4244) 

Camera::Camera(void)
	:m_vec3Position(0.0f,1.0f,0.0f)
	,m_vec3LookAt(0.0f,0.0f,0.0f)
	,m_vec3Up(0.0f,0.0f,-1.0f)
	,m_dfSpeed(5.0f)
	,m_dfPitchFactor(0.0f)
	,m_dfYawFactor(0.0f)
	,m_dfRollFactor(1.0f)
{
}


Camera::~Camera(void)
{
}

D3DXMATRIX Camera::GetViewMatrix()
{
	return m_matView;
}

void Camera::UpdateViewMatrix()
{
	D3DXMatrixLookAtLH(&m_matView, &m_vec3Position, &m_vec3LookAt, &m_vec3Up);
	GlobalMessage::Instance()->SetViewMatrix(m_matView);
}

void Camera::SetPosition( const D3DXVECTOR3& vec3 )
{
	m_vec3Position = vec3;
}

void Camera::SetLookAt( const D3DXVECTOR3& vec3 )
{
	m_vec3LookAt = vec3;
}

void Camera::SetUp( const D3DXVECTOR3& vec3 )
{
	m_vec3Up = vec3;
}

void Camera::MouseUpdate( MouseMsg msg,const Point& oldPoint,const Point& currPoint )
{
	switch (msg)
	{
	case MMsg_WheelForward:
		walk(Forward);
		break;
	case MMsg_WheelBackward:
		walk(Back);
		break;
	case MMsg_Move:
		mouseMove(oldPoint,currPoint);
		break;
	}
}

void Camera::walk( Direction direction )
{
	double speed = GlobalMessage::Instance()->GetTimeFromLastFrame()*40.0f*m_dfSpeed;

	D3DXVECTOR3 vec3Orientation = m_vec3LookAt - m_vec3Position;

	switch(direction)
	{
	case Forward:
		{
			D3DXVECTOR3	vec3Displacement = speed*vec3Orientation;
			m_vec3Position += vec3Displacement;
			m_vec3LookAt += vec3Displacement;
			break;
		}

	case Back:
		{
			D3DXVECTOR3	vec3Displacement = speed*vec3Orientation;
			m_vec3Position -= vec3Displacement;
			m_vec3LookAt -= vec3Displacement;
			break;
		}

	case Left:
		{
			D3DXVECTOR3 vec3Right;
			D3DXVec3Cross(&vec3Right,&m_vec3Up,&vec3Orientation);
			D3DXVECTOR3	vec3Displacement = speed*vec3Right;
			m_vec3Position -= vec3Displacement;
			m_vec3LookAt -= vec3Displacement;
			break;
		}

	case Right:
		{
			D3DXVECTOR3 vec3Right;
			D3DXVec3Cross(&vec3Right,&m_vec3Up,&vec3Orientation);
			D3DXVECTOR3	vec3Displacement = speed*vec3Right;
			m_vec3Position += vec3Displacement;
			m_vec3LookAt += vec3Displacement;
			break;
		}
	}
}

void Camera::Init()
{
	//make the camera listen the keyboard
	KeyboardSubject::Instance()->AddKeyboardListener(this);
	//make the camera listen the mouse
	MouseSubject::Instance()->AddMouseListener(this);
}

void Camera::KeyboardUpdate( KeyboadMsg msg )
{
	switch(msg)
	{
	case KMsg_W_DOWN:
		walk(Forward);
		break;
	case KMsg_S_DOWN:
		walk(Back);
		break;
	case KMsg_A_DOWN:
		walk(Left);
		break;
	case KMsg_D_DOWN:
		walk(Right);
		break;
	case KMsg_Z:
		roll(m_dfRollFactor);
		break;
	case KMsg_X:
		roll(-m_dfRollFactor);
		break;
	}
}

void Camera::mouseMove( const Point& oldPoint,const Point& currPoint )
{
	if(!MessageQueryer::Instance()->MouseRightButtonIsPress())
		return ;

	int dx,dy;
	dx = currPoint.x - oldPoint.x;
	dy = currPoint.y - oldPoint.y;

	pitch(dy * m_dfPitchFactor);
	yaw(dx * m_dfYawFactor);
}

void Camera::pitch( float angle )
{
	D3DXVECTOR3 vec3Right;
	D3DXMATRIX R;
	D3DXVec3Cross(&vec3Right,&m_vec3Up,&(m_vec3LookAt-m_vec3Position));

	D3DXVec3Normalize(&vec3Right,&vec3Right);

	D3DXMatrixRotationAxis(&R, &vec3Right, angle);
	D3DXVec3TransformNormal(&m_vec3LookAt, &m_vec3LookAt, &R);
	D3DXVec3TransformNormal(&m_vec3Up,&m_vec3Up,&R);

	D3DXVec3Normalize(&m_vec3Up,&m_vec3Up);
	//D3DXVec3Cross(&m_vec3Up,&vec3Right,&(m_vec3LookAt-m_vec3Position));
}

void Camera::yaw( float angle )
{
	D3DXMATRIX R;
	D3DXMatrixRotationAxis(&R, &m_vec3Up, angle);

	D3DXVec3TransformNormal(&m_vec3LookAt, &m_vec3LookAt, &R);
}

void Camera::roll( float angle )
{
	D3DXVECTOR3 vec3Orientation;
	D3DXVec3Normalize(&vec3Orientation , &(m_vec3LookAt-m_vec3Position));

	D3DXMATRIX R;
	D3DXMatrixRotationAxis(&R, &vec3Orientation, angle);
	D3DXVec3TransformNormal(&m_vec3Up,&m_vec3Up,&R);
}

void Camera::rotateY( float angle )
{
	D3DXMATRIX R;
	D3DXMatrixRotationY(&R, angle);

	D3DXVec3TransformNormal(&m_vec3Up, &m_vec3Up, &R);
	D3DXVec3TransformNormal(&m_vec3LookAt, &m_vec3LookAt, &R);
}

void Camera::SetPitchFactor( double factor )
{
	m_dfPitchFactor = factor;
}

void Camera::SetYawFactor( double factor )
{
	m_dfYawFactor = factor;
}

void Camera::SetSpeed( double speed )
{
	m_dfSpeed = speed;
}

void Camera::SetRollFactor( double factor )
{
	m_dfRollFactor = factor;
}

D3DXVECTOR3 Camera::GetPosition()
{
	return m_vec3Position;
}

D3DXVECTOR3 Camera::GetLookAt()
{
	return m_vec3LookAt;
}

D3DXVECTOR3 Camera::GetUp()
{
	return m_vec3Up;
}
